

#include "lua_guint.h"

/********************************************************************************/

static size_t guint_begin() {
  struct tm t = { 0 };
  t.tm_year   = 2024 - 1900;
  t.tm_mon    = 0;
  t.tm_mday   = 1;
  return (size_t)mktime(&t);
}

/********************************************************************************/

static unsigned short hash16(const void *data, size_t size){
  unsigned int cksum = 0;
  unsigned short *buff = (unsigned short*)data;
  while (size > 1) {
    cksum += *buff++;
    size -= sizeof(unsigned short);
  }
  if (size == 1)
    cksum += *((unsigned char*)buff);

  cksum = (cksum >> 16) + (cksum & 0xffff);
  cksum += (cksum >> 16);
  return (unsigned short)(~cksum);
}

/********************************************************************************/

static size_t host_hash() {
  error_code ec;
  auto host_name = hash_sha1(ip::host_name(ec));
  host_name += std::to_string(system_clock());
  host_name += std::to_string(getpid());
  return hash16(host_name.c_str(), host_name.size());
}

/********************************************************************************/

static const size_t HOST_HASH   = host_hash();
static const size_t OFFSET_TIME = guint_begin();

/********************************************************************************/

static int luac_guint(lua_State* L) {
  static std::atomic_ushort next(0);
  size_t now = time(0) - OFFSET_TIME;
  size_t id = (now << 32) | (HOST_HASH << 16) | next++;
  lua_pushinteger(L, id);
  return 1;
}

/********************************************************************************/

LUAOS_API int luaopen_guint(lua_State* L) {
  const luaL_Reg methods[] = {
    { "uuid",  luac_guint  },
    { NULL,     NULL        }
  };
  return new_module(L, "math", methods);
}

/********************************************************************************/
